package codeGeneration.persistent;

import java.util.*;

import util.Common;
import util.FileUtil;
import util.InformationForGenerate;

import lexer.TokenConstant;
import databaseTransaction.Command;
import databaseTransaction.Table;
import databaseTransaction.TableField;

public class JDBCPersistentGenerator extends PersistentGenerator {
	private String basePackageName;
	private InformationForGenerate informationForGenerate;

	// change almost
	public JDBCPersistentGenerator(Table table, String basePath,
			String packageName, String basePackageName,
			InformationForGenerate informationForGenerate) {
		super(table, table.getName(), packageName);
		persistentFileLocation = basePath + '/' + packageName.replace('.', '/');
		daoFileLocation = basePath + '/' + daoPackageName.replace('.', '/');
		this.basePackageName = basePackageName;
		this.informationForGenerate = informationForGenerate;

		informationForGenerate.setPackageOfDAO(daoPackageName);
		FileUtil.mkDir(persistentFileLocation);
		FileUtil.mkDir(daoFileLocation);
	}

	private void generateMapper() {
		indentOneTime("final class " + this.getClassName()
				+ "Mapper implements RowMapper", null,
				"<" + this.getClassName() + "> {", persistentBuffWriter, false);

		appendToFile(persistentBuffWriter, "@Override\n");
		indentOneTime("public " + this.getClassName(), null,
				" mapRow(ResultSet rs, int rowNum) " + "throws SQLException {",
				persistentBuffWriter, false);
		appendToFile(persistentBuffWriter, this.getClassName() + " obj = new "
				+ this.getClassName() + "();\n");

		// change
		List<TableField> allField = new ArrayList<TableField>(
				this.table.getId());
		allField.addAll(this.table.getTableFields());

		for (TableField tblField : allField) {
			String setName = tblField.getName().substring(0, 1).toUpperCase()
					+ tblField.getName().substring(1);

			switch (tblField.getType()) {
			case TokenConstant.INT:
				appendToFile(persistentBuffWriter, "obj.set" + setName
						+ "(rs.getInt(\"" + tblField.getName() + "\"));\n");
				break;
			case TokenConstant.DATE:
				appendToFile(persistentBuffWriter, "obj.set" + setName
						+ "(rs.getDate(\"" + tblField.getName() + "\"));\n");
				break;
			case TokenConstant.VARCHAR:
				appendToFile(persistentBuffWriter, "obj.set" + setName
						+ "(rs.getString(\"" + tblField.getName() + "\"));\n");
			}
		}

		appendToFile(persistentBuffWriter, "return obj;\n");
		decreaseIndentOneTime("}", persistentBuffWriter);
		decreaseIndentOneTime("}", persistentBuffWriter);
	}

	private void generateConstructor() {
		appendToFile(persistentBuffWriter, "\n");
		indentOneTime("public " + this.className + "Persistent()", null, "{",
				persistentBuffWriter, false);
		appendToFile(persistentBuffWriter, "mapper = new " + this.className
				+ "Mapper();\n");
		decreaseIndentOneTime("}", persistentBuffWriter);

	}

	private void generateSetDataSource() {
		persistentHeaderOfJavaFile
				.addImportsList("com.mysql.jdbc.jdbc2.optional.MysqlDataSource");
		persistentHeaderOfJavaFile
				.addImportsList("org.springframework.beans.factory.annotation.*");

		appendToFile(persistentBuffWriter, "\n");
		appendToFile(persistentBuffWriter, "@Autowired\n");
		indentOneTime("public void setDataSource(MysqlDataSource dataSource)",
				null, "{", persistentBuffWriter, false);
		appendToFile(persistentBuffWriter,
				"this.jdbcTemplate = new JdbcTemplate(dataSource);\n");
		decreaseIndentOneTime("}", persistentBuffWriter);
	}

	protected void generateDeclare() {
		persistentHeaderOfJavaFile.setPackageName(persistentPackageName);
		persistentHeaderOfJavaFile.addImportsList(basePackageName + ".bean."
				+ className);
		persistentHeaderOfJavaFile.addImportsList(daoPackageName + "."
				+ className + "DAO ");
		persistentHeaderOfJavaFile.addImportsList("java.util.*");
		persistentHeaderOfJavaFile.addImportsList("org.springframework."
				+ "stereotype.Repository");

		daoHeaderOfJavaFile.setPackageName(daoPackageName);
		daoHeaderOfJavaFile.addImportsList("java.util.*");
		daoHeaderOfJavaFile.addImportsList(basePackageName + ".bean."
				+ className);

		generateMapper();
		appendToFile(persistentBuffWriter, "\n");

		appendToFile(persistentBuffWriter, "@Repository\n");
		appendToFile(persistentBuffWriter, "public class " + className
				+ "Persistent implements " + className + "DAO {\n");
		indentOneTime("public interface " + className + "DAO ", null, "{",
				daoBuffWriter, false);

		informationForGenerate.setNameOfDAOClass(className + "DAO");
		appendToFile(persistentBuffWriter, "private " + this.className
				+ "Mapper mapper;\n");
		appendToFile(persistentBuffWriter,
				"private JdbcTemplate jdbcTemplate;\n");

		generateConstructor();
		generateSetDataSource();
	}

	protected void generateGetAll() {
		persistentHeaderOfJavaFile.addImportsList("java.sql.ResultSet");
		persistentHeaderOfJavaFile.addImportsList("java.sql.SQLException");
		persistentHeaderOfJavaFile
				.addImportsList("org.springframework.transaction.annotation.*");
		persistentHeaderOfJavaFile
				.addImportsList("org.springframework.jdbc.core.*");

		appendToFile(persistentBuffWriter, "\n");
		appendToFile(persistentBuffWriter, "@Transactional\n");

		String methodName = "getAll" + className + "()";
		indentOneTime("public List<" + this.className + "> " + methodName,
				null, "{", persistentBuffWriter, false);

		appendToFile(persistentBuffWriter, "List<" + this.className
				+ "> objects = new ArrayList<" + this.className + ">();\n");
		appendToFile(persistentBuffWriter, "String query = \"Select * from "
				+ this.className + "\";\n");
		appendToFile(persistentBuffWriter,
				"objects = this.jdbcTemplate.query(query, this.mapper);\n");
		appendToFile(persistentBuffWriter, "return objects;\n");
		decreaseIndentOneTime("}", persistentBuffWriter);

		appendToFile(daoBuffWriter, "List<" + this.className + "> "
				+ methodName + ";\n");
	}

	@Override
	protected void generateGetById() {
		if (this.getTable().getId().size() != 0) {
			String primaryField = "";
			String primaryType = "";

			String query = "select from " + this.className + " where ";
			String functionDeclare = "public " + className + " get" + className
					+ "ById(";
			for (TableField idField : this.getTable().getId()) {
				primaryField = idField.getName();
				primaryType = idField.parseType();

				query = query + primaryField + "=\\\"\"+" + primaryField
						+ "+\"\\\" and ";
				functionDeclare = functionDeclare + primaryType + " "
						+ primaryField + ", ";
			}

			query = query.substring(0, query.length() - 9);
			functionDeclare = functionDeclare.substring(0,
					functionDeclare.length() - 2)
					+ ")";

			appendToFile(persistentBuffWriter, "\n");
			appendToFile(persistentBuffWriter, "@Transactional\n");
			indentOneTime(functionDeclare, null, "{", persistentBuffWriter,
					false);
			appendToFile(persistentBuffWriter, "String query = \"" + query
					+ "+\"\\\"\";\n");

			appendToFile(
					persistentBuffWriter,
					this.className
							+ " result = this.jdbcTemplate.queryForObject(query, this.mapper);\n");
			appendToFile(persistentBuffWriter, "return result;\n");
			decreaseIndentOneTime("}", persistentBuffWriter);

			appendToFile(
					daoBuffWriter,
					functionDeclare.substring(functionDeclare.indexOf(' ') + 1,
							functionDeclare.length()) + ";\n");
		}
	}

	protected void generateAdd() {
		if (this.getTable().getId().size() != 0) {
			String objName = Common.convertFirstLetterToLowerCase(className);
			String functionDeclare = "public void add" + className + "("
					+ className + ' ' + objName + ")";
			String query = "String query = \"insert into " + this.className
					+ "(";
			List<TableField> allField = new ArrayList<TableField>(
					this.table.getId());
			allField.addAll(this.table.getTableFields());

			for (TableField idField : allField) {
				if (!idField.getExtra().contains(TokenConstant.AUTOINCREASE)) {
					query = query + idField.getName() + ", ";
				}
			}

			if (query.endsWith(", ")) {
				query = query.substring(0, query.length() - 2);
			}

			query = query + ") value(";

			for (TableField idField : allField) {
				if (!idField.getExtra().contains(TokenConstant.AUTOINCREASE)) {
					if (idField.getType() == TokenConstant.INT)
						query = query
								+ "\" + "
								+ objName
								+ ".get"
								+ Common.convertFirstLetterToUpperCase(idField
										.getName()) + "() + \", ";
					else
						query = query
								+ "\\\"\" + "
								+ objName
								+ ".get"
								+ Common.convertFirstLetterToUpperCase(idField
										.getName()) + "() + \"\\\", ";
				}
			}
			query = query.substring(0, query.length() - 2) + ")\";\n";

			appendToFile(persistentBuffWriter, "\n");
			appendToFile(persistentBuffWriter, "@Transactional\n");
			indentOneTime(functionDeclare, null, "{", persistentBuffWriter,
					false);
			appendToFile(persistentBuffWriter, query);
			appendToFile(persistentBuffWriter,
					"this.jdbcTemplate.execute(query);\n");
			decreaseIndentOneTime("}", persistentBuffWriter);

			appendToFile(
					daoBuffWriter,
					functionDeclare.substring(functionDeclare.indexOf(' ') + 1,
							functionDeclare.length()) + ";\n");
		}

	}

	protected void generateSet() {
		if (this.table.getId().size() > 0) {
			String primaryField = "";
			String primaryType = "";

			String query = "update " + this.className
					+ " set \"+fieldName+\" = \\\"\"+value+\"\\\" where ";
			String functionDeclare = "public void set(";

			for (TableField idField : this.getTable().getId()) {
				primaryField = idField.getName();
				primaryType = idField.parseType();

				query = query + primaryField + " = \\\"\"+" + primaryField
						+ "+\"\\\" and ";
				functionDeclare = functionDeclare + primaryType + " "
						+ primaryField + ", ";
			}

			query = query.substring(0, query.length() - 5) + "\";\n";
			functionDeclare = functionDeclare
					+ "String fieldName, String value)";

			appendToFile(persistentBuffWriter, "\n");
			appendToFile(persistentBuffWriter, "@Transactional\n");
			indentOneTime(functionDeclare, null, "{", persistentBuffWriter,
					false);

			appendToFile(persistentBuffWriter, "String query =  \"" + query);
			appendToFile(persistentBuffWriter,
					"this.jdbcTemplate.update(query);\n");
			decreaseIndentOneTime("}", persistentBuffWriter);

			appendToFile(
					daoBuffWriter,
					functionDeclare.substring(functionDeclare.indexOf(' ') + 1,
							functionDeclare.length()) + ";\n");
		}
	}

	protected void generateRemove() {
		if (this.getTable().getId().size() != 0) {
			String primaryField = "";
			String primaryType = "";

			String query = "delete from " + this.className + " where ";
			String functionDeclare = "public void remove" + className + "(";
			for (TableField idField : this.getTable().getId()) {
				primaryField = idField.getName();
				primaryType = idField.parseType();

				query = query + primaryField + "=\\\"\"+" + primaryField
						+ "+\"\\\" and ";
				functionDeclare = functionDeclare + primaryType + " "
						+ primaryField + ", ";
			}

			query = query.substring(0, query.length() - 9);
			functionDeclare = functionDeclare.substring(0,
					functionDeclare.length() - 2)
					+ ")";

			appendToFile(persistentBuffWriter, "\n");
			appendToFile(persistentBuffWriter, "@Transactional\n");
			indentOneTime(functionDeclare, null, "{", persistentBuffWriter,
					false);
			appendToFile(persistentBuffWriter, "String query = \"" + query
					+ "+\"\\\"\";\n");
			appendToFile(persistentBuffWriter,
					"this.jdbcTemplate.execute(query);\n");
			decreaseIndentOneTime("}", persistentBuffWriter);

			appendToFile(
					daoBuffWriter,
					functionDeclare.substring(functionDeclare.indexOf(' ') + 1,
							functionDeclare.length()) + ";\n");
		}
	}

	protected void generateRemoveAll() {
		appendToFile(persistentBuffWriter, "\n");
		appendToFile(persistentBuffWriter, "@Transactional\n");
		indentOneTime("public void removeAll()", null, "{",
				persistentBuffWriter, false);
		appendToFile(persistentBuffWriter, "String query = \"delete from "
				+ this.className + "\";\n");
		appendToFile(persistentBuffWriter,
				"this.jdbcTemplate.execute(query);\n");
		decreaseIndentOneTime("}", persistentBuffWriter);

		// DAO
		appendToFile(daoBuffWriter, "void removeAll();\n");
	}

	protected void generateGetAndSetBy() {
		String type = "";

		List<Command> commands = table.getCommands();

		for (Command command : commands) {
			TableField fieldName = command.getFieldName();
			List<TableField> listOfWhereFieldName = command
					.getListOfWhereFieldName();
			StringBuffer methodName = new StringBuffer();

			if (fieldName != null) {
				type = fieldName.parseType();
				if (type.equals("int"))
					type = "Integer";
				String upperCaseFieldName = ((char) (fieldName.getName()
						.charAt(0) - 32))
						+ fieldName.getName().substring(1,
								fieldName.getName().length());

				if (command.getTypeOfCommand() == TokenConstant.GET)
					methodName.append("public List<" + type + "> get"
							+ upperCaseFieldName + "By");
				if (command.getTypeOfCommand() == TokenConstant.SET)
					methodName.append("public void update" + upperCaseFieldName
							+ "By");

				for (int i = 0; i < listOfWhereFieldName.size(); ++i) {
					TableField tableField = listOfWhereFieldName.get(i);
					String whereFieldName = tableField.getName();
					whereFieldName = ((char) (whereFieldName.charAt(0) - 32))
							+ whereFieldName.substring(1,
									whereFieldName.length());

					methodName.append(whereFieldName);
					if (i < listOfWhereFieldName.size() - 1)
						methodName.append("And");
				}
				methodName.append('(');
				for (int i = 0; i < listOfWhereFieldName.size(); ++i) {
					TableField tableField = listOfWhereFieldName.get(i);
					methodName.append(tableField.parseType());
					methodName.append(' ');
					methodName.append(tableField.getName());

					if (i < listOfWhereFieldName.size() - 1)
						methodName.append(", ");
				}
				if (command.getTypeOfCommand() == TokenConstant.SET) {
					methodName.append(", ");
					methodName.append(fieldName.parseType());
					methodName.append(' ');
					methodName.append(fieldName.getName());
				}
				methodName.append(')');
				appendToFile(persistentBuffWriter, "\n");
				appendToFile(persistentBuffWriter, "@Transactional\n");

				appendToFile(daoBuffWriter, methodName.toString() + ";\n");
				indentOneTime(methodName.toString(), null, " {",
						persistentBuffWriter, false);

				if (command.getTypeOfCommand() == TokenConstant.GET)
					generateGetBy(fieldName, listOfWhereFieldName);
				if (command.getTypeOfCommand() == TokenConstant.SET)
					generateSetBy(fieldName, listOfWhereFieldName);
			}
		}
	}

	private void generateSetBy(TableField fieldName,
			List<TableField> listOfWhereFieldName) {
		StringBuffer query = new StringBuffer();
		query.append("\"update " + fieldName.getName() + " from "
				+ table.getName() + " set " + fieldName.getName()
				+ " = \\\"\" + " + fieldName.getName() + " + \"\\\" where ");
		for (int i = 0; i < listOfWhereFieldName.size(); ++i) {
			TableField tableField = listOfWhereFieldName.get(i);
			query.append(tableField.getName() + " = \\\"\" + "
					+ tableField.getName() + " + \"\\\"\"");

			if (i < listOfWhereFieldName.size() - 1)
				query.append(" + \" and \" + ");
		}

		appendToFile(persistentBuffWriter, "String query = " + query.toString()
				+ ";\n");

		appendToFile(persistentBuffWriter,
				"this.jdbcTemplate.query(query, this.mapper);\n");
		decreaseIndentOneTime("}", persistentBuffWriter);
	}

	private void generateGetBy(TableField fieldName,
			List<TableField> listOfWhereFieldName) {
		StringBuffer query = new StringBuffer();
		String type = fieldName.parseType();
		if (type.equals("int"))
			type = "Integer"; // "\"select " + abc + " from " + xyx + " where "
								// + 123 + " = "" + abc + """ +
								// "and 456 = "" + 456 + """

		query.append("\"select " + fieldName.getName() + " from "
				+ table.getName() + " where ");
		for (int i = 0; i < listOfWhereFieldName.size(); ++i) {
			TableField tableField = listOfWhereFieldName.get(i);
			query.append(tableField.getName() + " = \\\"\" + "
					+ tableField.getName() + " + \"\\\"\"");

			if (i < listOfWhereFieldName.size() - 1)
				query.append(" + \" and \" + ");
		}

		appendToFile(persistentBuffWriter, "List<" + type
				+ "> objects = new ArrayList<" + type + ">();\n");
		appendToFile(persistentBuffWriter, "String query = " + query.toString()
				+ ";\n");
		appendToFile(persistentBuffWriter,
				"objects = this.jdbcTemplate.queryForList(query, " + type
						+ ".class);\n");
		appendToFile(persistentBuffWriter, "return objects;\n");
		decreaseIndentOneTime("}", persistentBuffWriter);
	}
}